package com.bnzj.cloud.gateway.config;

import com.bnzj.cloud.gateway.external.CustomTokenAuthenticationWebFilter;
import com.bnzj.core.web.security.property.ResourcePathProperties;
import com.bnzj.core.webflux.security.CustomServerAuthenticationEntryPoint;
import com.bnzj.core.webflux.security.WebfluxAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.http.HttpMethod;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.authentication.ServerAuthenticationEntryPointFailureHandler;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * @ClassName OauthResourceHttpSecurityAdapter
 * @Description 当使用oauth2 resource 认证方式时，用来暴露springSecurityFilter
 *              过滤链中相关配置给业务系统使用。
 * @Author
 * @Date 2020/2/27
 * @Version V1.0
 **/
@Component
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnMissingBean(name = "customResourceHttpSecurityAdapter")
public class OauthResourceHttpSecurityAdapterWebflux {

    public OauthResourceHttpSecurityAdapterWebflux(){
        System.out.println("OauthResourceHttpSecurityAdapterWebflux");
    }

    @Autowired
    private ResourcePathProperties resourcePathProperties;

    @Autowired
    private WebfluxAccessDeniedHandler webfluxAccessDeniedHandler;

    @Autowired
    private CustomServerAuthenticationEntryPoint customServerAuthenticationEntryPoint;

    @Autowired
    private CustomTokenAuthenticationWebFilter thirdSysTokenAuthenticationWebFilter;

    @Autowired
    @Qualifier("customReactiveAuthorizationManager")
    private ReactiveAuthorizationManager<AuthorizationContext> customReactiveAuthorizationManager;


    /**
     * @Description oauth resource 通用配置
     * @param http:
     * @return: void
     * @Author
     * @Date 2020/2/27
     **/
    public void config(ServerHttpSecurity http){
        configBefore(http);
        http.csrf().disable();

        http.authorizeExchange().pathMatchers(HttpMethod.OPTIONS).permitAll();
        String[] permitPaths = resourcePathProperties.getPermitPath();
        String[] authenticatedPaths = resourcePathProperties.getAuthenticatedPath();
        String[] verifyPaths = resourcePathProperties.getVerifyPath();

        http.authorizeExchange().pathMatchers("/**").permitAll();
        //http.antMatcher("/api/idm/verify/*").authorizeRequests().antMatchers("/api/idm/verify/*").permitAll();
        if(!Objects.isNull(verifyPaths)){
            http.authorizeExchange().pathMatchers(verifyPaths).access(customReactiveAuthorizationManager);
        }
        if(!Objects.isNull(authenticatedPaths)){
            http.authorizeExchange().pathMatchers(authenticatedPaths).authenticated();
        }
        if(!Objects.isNull(permitPaths)){
            http.authorizeExchange().pathMatchers(permitPaths).permitAll();
        }
        http.oauth2ResourceServer().authenticationEntryPoint(customServerAuthenticationEntryPoint);
        http.oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt);
        http.exceptionHandling().accessDeniedHandler(webfluxAccessDeniedHandler)
        .authenticationEntryPoint(customServerAuthenticationEntryPoint);
        thirdSysTokenAuthenticationWebFilter.setAuthenticationFailureHandler(
                new ServerAuthenticationEntryPointFailureHandler(customServerAuthenticationEntryPoint));
        http.addFilterAfter(thirdSysTokenAuthenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);
        http.authorizeExchange().anyExchange().authenticated();
        configAfter(http);
    }

    /**
     * @Description 子类重写做定制配置
     * @param http:
     * @return: void
     * @Author
     * @Date 2020/2/27
     **/
    protected void configBefore(ServerHttpSecurity http){}

    /**
     * @Description 子类重写做定制配置
     * @param http:
     * @return: void
     * @Author
     * @Date 2020/2/27
     **/
    protected void configAfter(ServerHttpSecurity http){
    }

}
